package com.net128.beatportapps.downloader;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.net.URL;
import java.util.Calendar;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.EtchedBorder;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumn;

import org.apache.log4j.Logger;

import com.net128.beatportapps.TrackData;
import com.nilo.plaf.nimrod.NimRODLookAndFeel;

public class BeatMover extends JFrame implements Observer, StatusChangedObserver {
	private final static Logger log=Logger.getLogger(BeatMover.class);
	private static final String appIconName="app-icon.png";
	private static final long serialVersionUID = 1L;
	private DownloadsTableModel tableModel = new DownloadsTableModel();
	private JTable table;
	private JLabel downloadInfo;
	private JLabel statusMessage;
	private JProgressBar totalProgress;
	private String user;
	private String password;
	private static int maxDownloads=32;
	private String sessionCookies=null;
	private JButton downloadTracks;
	private JButton editPreferences;
	private JComboBox showType;
	private DownloadStatistics downloadStatistics=new DownloadStatistics();
	private Color BPGREEN=new Color(0x87bd0d);
	private boolean hasDownloads=false;
	private ImageIcon appIcon;
	private static String appName=BeatMover.class.getName();
	
	static {
	    int pos=appName.lastIndexOf('.');
	    if(pos>=0) {
	    	appName=appName.substring(pos+1);
	    }		
	}

	public static void main(String[] args) throws Exception {
		String user=null;
		String password=null;
		for(int i=0;i<args.length;i++) {
			if(args[i].equals("debug")) {
				BeatportAccessor.debug=true;
			} else {
				if(user==null) {
					user=args[i];
				} else {
					password=args[i];
				}
			}
		}
		new BeatMover().start(user, password);
	}
	
	private void start(String user, String password) throws Exception {
		UIManager.setLookAndFeel(NimRODLookAndFeel.class.getName());
		
		URL url = getClass().getClassLoader().getResource(appIconName);
		if(url == null) url = getClass().getResource(appIconName);
		if(url == null) url = new File(appIconName).toURI().toURL();
		appIcon=new ImageIcon(url);
		AppDialog.setAppImageIcon(appIcon.getImage());
		
		if(user==null) {
			LoginDialog loginDialog=new LoginDialog();
			sessionCookies=loginDialog.login();
			user=loginDialog.user;
			password=loginDialog.password;
		}
		init(user,password);
		setVisible(true);
	}

	public void init(String user, String password) {
		this.user=user;
		this.password=password;
	    setIconImage(new ImageIcon(appIcon.getImage().getScaledInstance(32, -1, Image.SCALE_SMOOTH)).getImage());

		setTitle(" "+appName);
		setLocation(50,50);
		setSize(820, 673);
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});

		JPanel addPanel = new JPanel(new FlowLayout(FlowLayout.LEFT,4,0));
		JPanel controlPanel = new JPanel(new BorderLayout());
		downloadTracks = new JButton("Download");
		downloadTracks.setEnabled(false);
		downloadTracks.setBackground(new Color(230,230,230));
		editPreferences = new JButton("Preferences...");
		showType = new JComboBox(new String [] {"new tracks", "re-downloadable tracks"});
		
		showType.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				showTracks();
			}
		});
		
		downloadTracks.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				enableButtons(false);
				actionDownloadAll();
			}
		});

		addPanel.add(showType);
		addPanel.add(downloadTracks);
		downloadInfo=new JLabel();
		addPanel.add(downloadInfo);

		editPreferences.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionEditPreferences();
			}
		});
		JPanel prefsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT,4,0));
		//prefsPanel.add(editPreferences);
		controlPanel.add(prefsPanel,BorderLayout.EAST);
		
		table = new ZebraTable(tableModel);
		MouseListener[] listeners = (MouseListener[])table.getListeners(MouseListener.class);
		for (int i = 0; i < listeners.length; i++)
			table.removeMouseListener( listeners[i] );
		table.getTableHeader().setReorderingAllowed(false);
		table.getTableHeader().setResizingAllowed(false);
		table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		table.setRowSelectionAllowed(false);
		table.setColumnSelectionAllowed(false);

		TableColumn column = table.getColumnModel().getColumn(1);
		column.setMaxWidth(140);
		column.setPreferredWidth(140);
		column = table.getColumnModel().getColumn(2);
		column.setPreferredWidth(90);
		column.setMaxWidth(90);
		
		DefaultTableCellRenderer cellRenderer=new DefaultTableCellRenderer();
		table.setDefaultRenderer(String.class, cellRenderer);
		
		table.getTableHeader().setDefaultRenderer(new TableHeaderCellRenderer()); 
		
		ProgressRenderer renderer = new ProgressRenderer(0, 100);
		renderer.setStringPainted(false); // show progress text
		table.setDefaultRenderer(JProgressBar.class, renderer);
		table.setRowHeight((int) renderer.getPreferredSize().getHeight());

		JPanel downloadsPanel = new JPanel();
		downloadsPanel.setLayout(new BorderLayout());
		downloadsPanel.add(new JScrollPane(table), BorderLayout.CENTER);
		controlPanel.add(addPanel,BorderLayout.WEST);

		JPanel statusPanel = new JPanel();
		statusMessage=new JLabel();
		totalProgress=new JProgressBar(0,100);
		totalProgress.setStringPainted(false);
		totalProgress.setForeground(statusMessage.getForeground());
		statusPanel.setLayout(new BorderLayout(6,6));
		statusPanel.add(new JLabel("Progress"),BorderLayout.WEST);
		statusPanel.add(totalProgress,BorderLayout.CENTER);
		statusPanel.add(statusMessage,BorderLayout.EAST);
		statusPanel.setBorder(BorderFactory.createEmptyBorder(8,8,8,8));
		controlPanel.add(statusPanel,BorderLayout.SOUTH);
		
		JPanel bpPanel = new JPanel(new BorderLayout());
		bpPanel.add(bpBox(controlPanel), BorderLayout.NORTH);
		bpPanel.add(bpBox(downloadsPanel), BorderLayout.CENTER);

		getContentPane().setLayout(new GridLayout(1,1));
		getContentPane().add(bpPanel);
		
		SwingUtilities.invokeLater(new Runnable() {public void run() {showTracks();}});
	}
	
	private JPanel bpPanel(Component component, boolean etchedBorder) {
		JPanel bpPanel = new JPanel(new GridLayout(1,1,0,0));
		bpPanel.add(component);
		int border=4;
		if(etchedBorder) {
			bpPanel.setBorder(BorderFactory.createEtchedBorder());
		} else {
			bpPanel.setBorder(BorderFactory.createEmptyBorder(border, border, border, border));
		}
		bpPanel.setBackground(BPGREEN);
		return bpPanel;
	}
	
	private Component bpBox(Component component) {
		JPanel panel=new JPanel(new GridLayout(1,1));
		int border=5;
		panel.setBorder(BorderFactory.createEmptyBorder(border, border, border, border));
		panel.add(component);
		JPanel panel2=new JPanel(new GridLayout(1,1));
		panel2.add(bpPanel(bpPanel(bpPanel(panel, false),true),false));
		panel2.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
		return panel2;
	}

	private void postShowTracks() {
		DownloadWorker.initTotals();
		int index=showType.getSelectedIndex();
		actionAddAll(index==0);
		enableButtons(true);
		if(!hasDownloads) {
			downloadTracks.setEnabled(false);
		}
		tableModel.sortingChanged();
		updateStatus();
	}

	private void showTracks() {
		enableButtons(false);
		downloadInfo.setText("Loading track info...");
		SwingUtilities.invokeLater(new Runnable() {public void run() {postShowTracks();}});
	}
	
	private void enableButtons(boolean enable) {
		if(tableModel.getRowCount()>0) {
			downloadTracks.setEnabled(enable);
		}
		if(enable) {
			statusMessage.setText("");
		}
		editPreferences.setEnabled(enable);
		showType.setEnabled(enable);
	}
	
	private void actionEditPreferences() {
		new PreferencesDialog().edit();
	}

	private String getFileName(TrackData trackData) {
		String name=trackData.getName();
		Calendar calendar=Calendar.getInstance();
		calendar.setTime(trackData.getDateAdded());
		int yearAdded=calendar.get(Calendar.YEAR);
		calendar.setTime(trackData.getDateReleased());
		int yearReleased=calendar.get(Calendar.YEAR);
		String mixName=trackData.getMixName();
		if("Original Mix".equals(mixName)) {
			mixName="";
		} else if(mixName.length()>0){
			mixName=" ("+mixName+")";
		}
		mixName=mixName.replaceAll(", ", "+");
		String labelName=trackData.getLabel();
		String artistName=trackData.getArtist();
		String trackName=yearAdded+"__www_beatport_com ("+labelName+")__"+yearReleased+"__"+
			artistName+" - "+name+mixName;
		trackName=trackName.replace("&", "+");
		trackName=trackName.replaceAll("[.?]", "");
		trackName=trackName.replaceAll("__www_beatport_com", "__www.beatport.com");
		trackName=trackName.replaceAll(" [+] ", "+");
		trackName=trackName.replaceAll("\\(INACTIVE\\)","");
		trackName=trackName.toLowerCase();
		trackName=trackName.replaceAll("remix", "rmx");
		trackName+="."+trackData.getFormat();
		return trackName;
	}

	private void actionDownloadAll() {
		downloadStatistics=new DownloadStatistics();
		int m=0;
		DownloadWorker [] downloadWorkers=tableModel.getDownloads();
		for(int i=0;i<downloadWorkers.length;i++) {
			DownloadWorker downloadWorker=downloadWorkers[i];
			if(downloadWorker.getStatus()==DownloadWorker.WAITING) {
				System.out.println("Resuming: "+downloadWorker.getFile());
				downloadWorker.resume();
				tableModel.sortingChanged();
				if(++m>=maxDownloads) {
					break;
				}
			}
		}
	}

	private void actionAddAll(boolean newTracks) {
		try {
			hasDownloads=false;
			for (int i = tableModel.getRowCount(); i > 0; --i) {
				tableModel.clearDownload(i - 1);
			}
			List<TrackData> trackDataList;
			if (newTracks) {
				trackDataList = new BeatportAccessor().getAvailableTracks(sessionCookies);
			} else {
				trackDataList = new BeatportAccessor().getPreviousTracks(sessionCookies);
			}
			int n = trackDataList.size();
			String text;
			if (trackDataList.size() == 0) {
				downloadTracks.setEnabled(false);
				text = "no tracks to download";
			} else {
				downloadTracks.setEnabled(true);
				text = n + " track";
				if (trackDataList.size() > 1) {
					text += "s";
				}
			}
			downloadInfo.setText(text);
			for (int i = 0; i < trackDataList.size(); i++) {
				TrackData trackData = trackDataList.get(i);
				String fileName = getFileName(trackData);
				URL url = new URL(trackData.getUrl());
				//System.out.println(fileName + "  " + url);
				DownloadWorker downloadWorker = new DownloadWorker(url,
						fileName, user, password, this, trackData.getSize());
				if(downloadWorker.getStatus()==DownloadWorker.WAITING) {
					hasDownloads=true;
				}
				tableModel.addDownload(downloadWorker);
			}
		} catch (Exception e) {
			log.error("Error adding all tracks", e);
		}
	}

	public void update(Observable o, Object arg) {
	}
	
	private static synchronized boolean intStatusChanged(int status, DownloadsTableModel tableModel) {
		boolean enableButtons=false;
		if(status==DownloadWorker.ERROR) {
			tableModel.sortingChanged();
		} else if(status==DownloadWorker.COMPLETE) {
			enableButtons=true;
			DownloadWorker [] downloadWorkers=tableModel.getDownloads();
			for(int i=0;i<downloadWorkers.length;i++) {
				DownloadWorker downloadWorker=downloadWorkers[i];
				if(downloadWorker.getStatus()==DownloadWorker.WAITING) {
					downloadWorker.resume();
					tableModel.sortingChanged();
					enableButtons=false;
					break;
				} else if(downloadWorker.getStatus()==DownloadWorker.DOWNLOADING) {
					enableButtons=false;
				}
			}
		}
		return enableButtons;
	}
	
	private void updateStatus() {
		long downloaded=DownloadWorker.getToalDownloaded();
		long total=DownloadWorker.getTotal();
		totalProgress.setValue((int)((downloaded*100.0)/total));
		statusMessage.setText(downloadStatistics.getETAStringFull(downloaded,total));
	}

	public void statusChanged(int status) {
		if(DownloadWorker.getTotal()>0) {
			updateStatus();
		}
		if(intStatusChanged(status,tableModel)) {
			enableButtons(true);
			downloadTracks.setEnabled(false);
		}
	}
}
